package jmine.tec.utils.reflection.fast.asm;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import jmine.tec.utils.reflection.fast.AbstractFastClass;
import jmine.tec.utils.reflection.fast.FastConstructor;
import jmine.tec.utils.reflection.fast.FastMethod;

/**
 * @author takeshi
 */
public abstract class GeneratedFastClassAccessor extends AbstractFastClass {

    private final Class<?> type;

    private final List<FastMethod> methods;

    private final List<FastConstructor> constructors;

    /**
     * C'tor
     * 
     * @param type {@link Class}
     */
    public GeneratedFastClassAccessor(Class<?> type) {
        super();
        this.type = type;
        this.methods = Collections.unmodifiableList(computeMethods(type, this));
        this.constructors = Collections.unmodifiableList(computeConstructors(type, this));
    }

    /**
     * {@inheritDoc}
     */
    public Collection<? extends FastConstructor> getConstructors() {
        return this.constructors;
    }

    /**
     * Se o metodo deve ser incluido. Filtra os metodos nao publicos.
     * 
     * @param m {@link Method}
     * @return boolean
     */
    public static boolean includeMethod(Method m) {
        return Modifier.isPublic(m.getModifiers());
    }

    /**
     * Computa os metodos
     * 
     * @param type {@link Class}
     * @param gen {@link GeneratedFastClassAccessor}
     * @return {@link List} of {@link FastMethod}
     */
    private static List<FastMethod> computeMethods(Class<?> type, GeneratedFastClassAccessor gen) {
        Method[] methods2 = type.getMethods();
        List<FastMethod> list = new ArrayList<FastMethod>(methods2.length);
        for (int i = 0; i < methods2.length; i++) {
            Method method = methods2[i];
            final int numParams = method.getParameterTypes().length;
            switch (numParams) {
            case 0:
                list.add(new FastMethodNoArgImpl(i, method, gen));
                break;

            case 1:
                list.add(new FastMethodSingleArg(i, method, gen));
                break;

            default:
                list.add(new FastMethodImpl(i, method, gen));
                break;
            }
        }
        return list;
    }

    /**
     * @param type {@link Class}
     * @param gen {@link GeneratedFastClassAccessor}
     * @return {@link List} de {@link FastConstructor}
     */
    private static List<FastConstructor> computeConstructors(Class<?> type, GeneratedFastClassAccessor gen) {
        final Constructor<?>[] ctors = type.getConstructors();
        List<FastConstructor> fs = new ArrayList<FastConstructor>(ctors.length);
        int i = 0;
        for (Constructor<?> ctor : ctors) {
            fs.add(new AsmFastConstructor(ctor, i++, gen));
        }
        return fs;
    }

    /**
     * Faz a invocacao de um metodo sem argumentos
     * 
     * @param index int
     * @param target alvo
     * @return Object
     * @throws Exception e
     */
    public Object invokeMethodNoArgs(int index, Object target) throws Exception {
        throw new NoSuchMethodError("method not found with a single arg");
    }

    /**
     * Faz a invocacao de um metodo com apenas um argumento
     * 
     * @param index o indice
     * @param target o alvo
     * @param args o argumento
     * @return Object
     * @throws Exception e
     */
    public Object invokeMethodSingleArg(int index, Object target, Object args) throws Exception {
        throw new NoSuchMethodError("method not found with a single arg");
    }

    /**
     * Faz a invocacao de um metodo com multiplos argumentos
     * 
     * @param index o indice do metodo
     * @param target o alvo
     * @param args os argumentos
     * @return Object
     * @throws Exception e
     */
    public abstract Object invokeMethodMultipleArgs(int index, Object target, Object[] args) throws Exception;

    /**
     * Faz a invocacao de um construtor
     * 
     * @param index indice
     * @param args arguments
     * @return Object
     * @throws Exception e
     */
    public Object invokeConstructor(int index, Object[] args) throws Exception {
        return null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final String toString() {
        return "generated accessor for: " + this.type;
    }

    /**
     * {@inheritDoc}
     */

    @Override
    public final int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((this.type == null) ? 0 : this.type.hashCode());
        return result;
    }

    /**
     * Lanca uma excecao
     * 
     * @param index int
     * @return {@link Object}
     */
    protected final Object error(int index) {
        throw new NoSuchMethodError("method not found: " + index);
    }

    /**
     * {@inheritDoc}
     */

    @Override
    public final boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (this.getClass() != obj.getClass()) {
            return false;
        }
        GeneratedFastClassAccessor other = (GeneratedFastClassAccessor) obj;
        return this.type == other.type;
    }

    /**
     * {@inheritDoc}
     */
    public final Collection<? extends FastMethod> getMethods() {
        return this.methods;
    }

    /**
     * {@inheritDoc}
     */
    public Class<?> getType() {
        return this.type;
    }

}
